// AffixFilterDlg.cpp : implementation file
//
#ifndef OBLIVION

#include "stdafx.h"
#include "AffixFilterDlg.h"
#include "afxdialogex.h"

#include "API/TES/TESDataHandler.h"
#include "API/CSDialogs/TESDialog.h"
#include "CSVparser.h"
#include "AffixFilter.h"
#include "MagicItemGenerator.h"

#include "ItemTab.h"
#include "RareTab.h"
#include "HelpTab.h"

#include "Utilities.h"
#include "Globals.h"

#include <string>

// CAffixFilterDlg dialog

IMPLEMENT_DYNAMIC(CAffixFilterDlg, CDialog)

CAffixFilterDlg::CAffixFilterDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CAffixFilterDlg::IDD, pParent)
	, app(FALSE)
	, arm(FALSE)
	, wep(FALSE)
	, EDID(_T(""))
	, currPtr(NULL)
	, mod(false)
	, currTab(-1)
{
	tabs[0] = new CItemTab(this);
	tabs[1] = new CRareTab(this);
	tabs[2] = new CHelpTab(this);
}

CAffixFilterDlg::~CAffixFilterDlg()
{
	if ( AffixFilter::fltDlg == this ) AffixFilter::fltDlg = NULL;

	CDialog::~CDialog();
}

void CAffixFilterDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_MIG, CMigGrid);
	DDX_Text(pDX, IDC_FLT_EDITORID, EDID);
	DDX_Control(pDX, IDC_FLTLIST, CFltList);
	DDX_Control(pDX, IDC_FLT_EDITORID, CEditorID);
	DDX_Control(pDX, IDAPPLY, CApply);
	DDX_Control(pDX, IDC_TABS, CFilterTabs);
}


BEGIN_MESSAGE_MAP(CAffixFilterDlg, CDialog)
	ON_NOTIFY(LVN_GETDISPINFO, IDC_FLTLIST, &CAffixFilterDlg::OnLvnGetdispinfoFltlist)
	ON_EN_CHANGE(IDC_FLT_EDITORID, &CAffixFilterDlg::OnModify)
	ON_BN_CLICKED(IDOK, &CAffixFilterDlg::OnBnClickedOk)
	ON_BN_CLICKED(IDAPPLY, &CAffixFilterDlg::OnBnClickedApply)
	ON_BN_CLICKED(IDCANCEL, &CAffixFilterDlg::OnBnClickedCancel)
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_NOTIFY(NM_DBLCLK, IDC_FLTLIST, &CAffixFilterDlg::OnNMDblclkFltlist)
	ON_NOTIFY(GVN_MODIFYITEM, IDC_MIG, &CAffixFilterDlg::OnNMGridModifiedMig)
	ON_NOTIFY(NM_RCLICK, IDC_FLTLIST, &CAffixFilterDlg::OnNMRClickFltlist)
	ON_NOTIFY(LVN_KEYDOWN, IDC_FLTLIST, &CAffixFilterDlg::OnLvnKeydownFltlist)
	ON_BN_CLICKED(IDC_IMPORT, &CAffixFilterDlg::OnBnClickedImport)
	ON_NOTIFY(TCN_SELCHANGE, IDC_TABS, &CAffixFilterDlg::OnTcnSelchangeTabs)
END_MESSAGE_MAP()


// CAffixFilterDlg message handlers


BOOL CAffixFilterDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// initialize the AffixFilter list in all its too-many-lines glory

	// some styling...
	CFltList.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
	
	// columns...
	LVCOLUMN Col;
	Col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	Col.fmt = LVCFMT_LEFT;
	Col.cx = 70;
	Col.pszText = "EditorID";
	Col.iSubItem = 0;
	CFltList.InsertColumn(0, &Col);

	Col.cx = 5;
	Col.pszText = "FormID";
	Col.iSubItem = 1;
	CFltList.InsertColumn(1, &Col);

	Col.cx = 65;
	Col.pszText = "Type";
	Col.iSubItem = 2;
	CFltList.InsertColumn(2, &Col);

	// and finally the AffixFilters themselves...
	LVITEM lv;
	lv.iItem = -1;
	lv.iSubItem = 0;
	lv.mask = LVIF_TEXT|LVIF_PARAM;
	std::string edid;
	for ( BSSimpleList<TESForm*>::Node* i = &AffixFilter::extendedForm.FormList().firstNode; i && i->data; i = i->next ) {
		AffixFilter* loadAffixFilter = dynamic_cast<AffixFilter*>(i->data);
		if ( loadAffixFilter ) {
			edid = loadAffixFilter->GetEditorID();
			if ( loadAffixFilter->formFlags & TESForm::kFormFlags_FromActiveFile )
				edid += " *";
			if ( loadAffixFilter->formFlags & TESForm::kFormFlags_Deleted )
				edid += " D";
			lv.pszText = (LPSTR)edid.c_str();
			lv.cchTextMax = edid.size();
			lv.lParam = (LPARAM)loadAffixFilter;
			CFltList.InsertItem(&lv);
			// other subitems should be handled by the GetDispInfo function
		}
		else // i->data is wrong polymorphic type, which is just lolwut.
			_ERROR("Bad AffixFilter '%s'?", i->data->GetEditorID());
	}


	// tackling the thing that started this mess, the MIG list.
	// so. we have a CGridCtrl. It's... spiffy? hopefully.

	// set various options
	CMigGrid.AllowReorderColumn(false);
	CMigGrid.EnableDragRowMode(false);
	CMigGrid.SetGridLines(GVL_BOTH);
	CMigGrid.SetEditable(FALSE);
	CMigGrid.SetListMode(TRUE);
	CMigGrid.EnableSelection(FALSE);
	CMigGrid.SetColumnCount(10);
	CMigGrid.EnableDragAndDrop(FALSE);
	CMigGrid.SetRowResize(FALSE);
	CMigGrid.SetColumnResize(TRUE);
	CMigGrid.SetHandleTabKey(FALSE);
	CMigGrid.SetTrackFocusCell(FALSE);
	CMigGrid.EnableTitleTips(FALSE);
	CMigGrid.SetHeaderSort(TRUE);
	CMigGrid.SetBkColor(RGB(0xFF, 0xFF, 0xFF)); // SetBkColor is deprecated, but I don't see a replacement for it.

	// set initial rows & columns
	CMigGrid.SetColumnCount(1);
	CMigGrid.SetFixedColumnCount(0);
	CMigGrid.SetRowCount(1);
	CMigGrid.SetFixedRowCount(1);

	CMigGrid.SetRowHeight(0, 18);

	// add column headers
	CString column_headings[] = {
		"EditorID", "FormID", "Type",
		"Subpetty", "Petty", "Lesser", "Common", "Greater", "Grand", "Ultragrand", "Negative"
	};
	int column_widths[] = {
		60, 5, 25,
		55, 55, 55, 55, 55, 55, 55, 20
	};
	BOOST_STATIC_ASSERT_MSG(lengthof(column_headings) == lengthof(column_widths), "column_headings and column_widths do not have the same length.");
	CMigGrid.SetColumnCount(lengthof(column_headings));

	GV_ITEM gv;
	gv.row = 0;
	gv.mask = GVIF_TEXT;
	for (gv.col = 0; gv.col < CMigGrid.GetColumnCount(); ++gv.col) {
		gv.strText = _T(column_headings[gv.col]);
		CMigGrid.SetItem(&gv);
		CMigGrid.SetColumnWidth(gv.col, column_widths[gv.col]);
	}

	// fill in MIGs
	for ( BSSimpleList<TESForm*>::Node* i = &MagicItemGenerator::extendedForm.FormList().firstNode; i && i->data; i = i->next ) {
		MagicItemGenerator* loadMig = dynamic_cast<MagicItemGenerator*>(i->data);
		if ( loadMig ) {
			CMigGrid.SetRowCount(++gv.row + 1);

			gv.col = 0;
			gv.mask = GVIF_TEXT|GVIF_PARAM;
			gv.strText = loadMig->GetEditorID();
			gv.lParam = (LPARAM)loadMig;
			CMigGrid.SetItem(&gv);

			++gv.col;
			gv.strText.Format("%08X", loadMig->formID);
			CMigGrid.SetItem(&gv);

			++gv.col;
			gv.strText = (loadMig->suf?"Suffix":"Prefix");
			CMigGrid.SetItem(&gv);

			BSStringT* affixes[] = {
				&loadMig->sub, &loadMig->pet, &loadMig->les, &loadMig->com, &loadMig->grt, &loadMig->gnd, &loadMig->ult,
				&loadMig->neg
			};
			for ( ++gv.col; gv.col < CMigGrid.GetColumnCount(); ++gv.col) {
				CMigGrid.SetCellType(gv.row, gv.col, RUNTIME_CLASS(CGridCellCheck));
				if ( affixes[gv.col-3]->Size() ) {
					std::string affix = affixes[gv.col-3]->c_str();
					for ( std::string::iterator i = affix.begin(); i != affix.end(); ++i ) {
						if ( isalpha(*i) && *i == toupper(*i) ) {
							if ( i != affix.begin() )
								affix.erase(affix.begin(), --i);
							break;
						}
					}
					gv.strText = affix.c_str();
				}
				else {
					gv.strText = "";
					CGridCellCheck* cell = (CGridCellCheck*)CMigGrid.GetCell(gv.row, gv.col);
					if ( cell )
						cell->SetCheckReadOnly(TRUE);
					else
						_ERROR("Cell not check format?");
				}
				CMigGrid.SetItem(&gv);
			}
		}
		else
			_ERROR("Bad MagicItemGenerator '%s'?", i->data->GetEditorID());
	}
	CMigGrid.ExpandLastColumn();
	CMigGrid.SortTextItems(0, TRUE);

	tabs[0]->Create(IDD_FLTDLG_ITEM, this);
	tabs[1]->Create(IDD_FLTDLG_RARE, this);
	tabs[2]->Create(IDD_FLTDLG_HELP, this);

	CFilterTabs.InsertItem(0, (LPCTSTR)"Item");
	CFilterTabs.InsertItem(1, (LPCTSTR)"Rares");
	CFilterTabs.InsertItem(2, (LPCTSTR)"Help");
	ShowTab(0);

	CEditorID.SetFocus();
	dlgMod(false, true);

	UpdateWindow();
	Invalidate();

	return FALSE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}


void CAffixFilterDlg::OnLvnGetdispinfoFltlist(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
	// TODO: Add your control notification handler code here
	AffixFilter* flt = (AffixFilter*)pDispInfo->item.lParam;
	if ( flt ) {
		switch (pDispInfo->item.iSubItem) {
			case 0: // EditorID
				sprintf_s(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, "%s%s%s", flt->GetEditorID(), (flt->formFlags & TESForm::kFormFlags_FromActiveFile?" *":""), (flt->formFlags & TESForm::kFormFlags_Deleted?" D":""));
				break;

			case 1: // FormID
				sprintf_s(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, "%08X", flt->formID);
				break;

			case 2: // Type
				if ( flt->app && flt->wep )
					sprintf_s(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, "Both");
				else if ( flt->app )
					sprintf_s(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, "Apparel");
				else if ( flt->wep )
					sprintf_s(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, "Weapon");
				else
					sprintf_s(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, "** NONE! **");
				break;

			default:
				_ERROR("Unknown subitem #%i requested!", pDispInfo->item.iSubItem);
				break;
		}
	}
	*pResult = 0;
}


void CAffixFilterDlg::OnModify()
{
	dlgMod(true);
}


void CAffixFilterDlg::OnBnClickedApply()
{
	if ( dlgSave() )
		dlgMod(false);
}


void CAffixFilterDlg::dlgMod(bool new_mod, bool force /*=false*/) {
	if ( new_mod != mod || force ) {
		std::string new_title = "AffixFilter - [";
		if ( currPtr ) {
			new_title += currPtr->GetEditorID();
			if ( currPtr->formFlags & TESForm::kFormFlags_FromActiveFile )
				new_title += " *";
			if ( currPtr->formFlags & TESForm::kFormFlags_Deleted )
				new_title += " D";
		}
		else
			new_title += " *";
		new_title += "]";
		if ( new_mod )
			new_title += " *";
		SetWindowText(new_title.c_str());

		CApply.EnableWindow(new_mod);
		mod = new_mod;

		if ( !mod )
			CMigGrid.SetModified(FALSE);

		UpdateWindow();
		Invalidate(FALSE);
	}
}

void CAffixFilterDlg::OnBnClickedOk()
{
	_DMESSAGE("OKing AffixFilter dialog.");
	if ( dlgSave() ) {
		AffixFilter::fltDlg = NULL;
		CDialog::OnOK();
	}
}


void CAffixFilterDlg::OnBnClickedCancel()
{
	_DMESSAGE("Canceling AffixFilter dialog.");
	if ( dlgConfirm() ) {
		AffixFilter::fltDlg = NULL;
		CDialog::OnCancel();
	}
}

bool CAffixFilterDlg::dlgSave()
{
	_VMESSAGE("Saving");
	if ( mod ) {
		char buf[0x200];
		CEditorID.GetWindowText(buf, sizeof(buf));
		if ( buf[0] == 0 ) {
			MessageBox("A form must have an EditorID.", "Empty EditorID", MB_OK|MB_ICONWARNING);
			_WARNING("Attempted to save without EditorID; form not saved.");
			CEditorID.SetFocus();
			return false;
		}

		if ( TESForm::SanitizeEditorIDString(buf) )
			CEditorID.SetWindowText(buf);

		_VMESSAGE("EDID: '%s'", buf);
	
		bool changedID = false;
		TESForm* tes = TESForm::LookupByEditorID(buf);
		if ( tes ) {
			if ( tes != currPtr ) {
				std::string prompt = std::string("Form's ID is not unique!\nPrevious form is type ") + TESForm::GetFormTypeName(tes->GetFormType());
				MessageBox((LPCSTR)prompt.c_str(), (LPCSTR)"Form Editor ID Error", MB_OK|MB_ICONWARNING);
				return false;
			}
		}
		else if ( currPtr ) {
			std::string prompt = std::string("Old ID: ") + currPtr->GetEditorID() + " New ID: "+ buf + "\nYou have changed the Form's Editor ID.\n\nCreate new form?";
			if ( MessageBox((LPCSTR)prompt.c_str(), (LPCSTR)"Create New Object?", MB_YESNO|MB_ICONQUESTION) == IDYES )
				currPtr = NULL;
			else
				changedID = true;
		}

		if ( !currPtr ) {
			_VMESSAGE("New form");
			currPtr = new AffixFilter();
			if ( !currPtr )
				_ERROR("currPtr null?!");

			_VMESSAGE("Setting from active file.");
			currPtr->SetFromActiveFile(true);
			_VMESSAGE("Setting editor ID.");
			currPtr->SetEditorID(buf);
			_VMESSAGE("Reserving form ID.");
			currPtr->SetFormID(TESDataHandler::dataHandler->ReserveNextUnusedFormID());
			_VMESSAGE("Adding to data handler.");
			TESDataHandler::dataHandler->AddFormToHandler(static_cast<TESForm*>(currPtr));

			LVITEM lv;
			lv.mask = LVIF_TEXT|LVIF_PARAM;
			lv.iItem = -1;
			lv.iSubItem = 0;
			lv.lParam = (LPARAM)currPtr;
			std::string edid = std::string(currPtr->GetEditorID()) + " *";
			lv.pszText = (LPSTR)edid.c_str();
			lv.cchTextMax = edid.size();
			_VMESSAGE("Adding to list.");
			CFltList.InsertItem(&lv);
		}
		else {
			changedID |= ((currPtr->formFlags & TESForm::kFormFlags_FromActiveFile) == 0);
			currPtr->SetFromActiveFile(true);
			if ( changedID ) {
				_VMESSAGE("EDID changed");
				currPtr->SetEditorID(buf);
				std::string edid = currPtr->GetEditorID();
				edid += (currPtr->formFlags & TESForm::kFormFlags_FromActiveFile?" *":"");
				edid += (currPtr->formFlags & TESForm::kFormFlags_Deleted?" D":"");

				LVFINDINFO lvf;
				lvf.flags = LVFI_PARAM;
				lvf.lParam = (LPARAM)currPtr;

				CFltList.SetItemText(CFltList.FindItem(&lvf), 0, edid.c_str());
			}
		}
		_VMESSAGE("Getting data from dialog.");
		currPtr->GetFromDialog(GetSafeHwnd());
	}
	return true;
}


bool CAffixFilterDlg::dlgConfirm()
{
	if ( mod ) {
		char buf[0x200];
		CEditorID.GetWindowText(buf, sizeof(buf));
		std::string prompt = std::string("AffixFilter '") + buf + "' has been modified.\n\nSave current changes? ('No' will discard changes)";
		switch ( MessageBox((LPCSTR)prompt.c_str(), (LPCSTR)"Save AffixFilter?", MB_YESNOCANCEL) ) {
			case IDYES:
				return dlgSave();

			case IDNO:
				dlgMod(false);
				return true;

			case IDCANCEL:
				return false;
		}
	}
	return true;
}


void CAffixFilterDlg::OnClose()
{
	_DMESSAGE("Closing AffixFilter dialog.");
	if ( dlgConfirm() ) {
		AffixFilter::fltDlg = NULL;
		CDialog::OnClose();
	}
}


void CAffixFilterDlg::OnDestroy()
{
	_DMESSAGE("Destroying AffixFilter dialog.");
	CDialog::OnDestroy();

	AffixFilter::fltDlg = NULL;
}


void CAffixFilterDlg::PostNcDestroy()
{
	_DMESSAGE("AffixFilter dialog has been destroyed.");
	AffixFilter::fltDlg = NULL;

	CDialog::PostNcDestroy();
}


void CAffixFilterDlg::OnNMDblclkFltlist(NMHDR *pNMHDR, LRESULT *pResult)
{
	if ( !dlgConfirm() )
		return;

	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

	LVHITTESTINFO ht;
	ht.flags = LVHT_ONITEM;
	ht.pt = pNMItemActivate->ptAction;
	int item = CFltList.HitTest(&ht);
	
	if ( !dlgEdit(pNMItemActivate->ptAction) )
		dlgNew();

	*pResult = 0;
}


bool CAffixFilterDlg::dlgNew() {
	if ( !dlgConfirm() )
		return false;

	AffixFilter::currPtr = currPtr = NULL;
	CEditorID.SetWindowText(_T(""));

	EDID = _T("");
	currPtr = NULL;
	mod = false;

	app = FALSE;
	arm = FALSE;
	wep = FALSE;

	for ( int i = 0; i < pageCt; ++i ) {
		if ( !tabs[i]->New() ) {
			_ERROR("Tab #%i failed to clear for new filter.", i);
		}
	}

	for ( UInt32 i = 0, k = 0; i < CMigGrid.GetRowCount(); ++i ) {
		for ( UInt32 j = 3; j < 11; ++j ) {
			CGridCellCheck* cell = dynamic_cast<CGridCellCheck*>(CMigGrid.GetCell(i, j));
			if ( cell && cell->IsCheckReadOnly() == FALSE )
				cell->SetCheck(BST_UNCHECKED);
		}
	}

	CEditorID.SetFocus();

	dlgMod(false, true);

	return true;
}

bool CAffixFilterDlg::dlgEdit(POINT pt)
{
	LVHITTESTINFO ht;
	ht.flags = LVHT_ONITEM;
	ht.pt = pt;
	return dlgEdit(CFltList.HitTest(&ht));
}


bool CAffixFilterDlg::dlgEdit(int item)
{
	if ( item >= 0 ) {
		if ( dlgConfirm() ) {
			AffixFilter* flt = (AffixFilter*)CFltList.GetItemData(item);
			if ( flt ) {
				flt->SetInDialog(GetSafeHwnd());
				return true;
			}
			else {
				_ERROR("Nulled FltList lParam on row %i.", item);
			}
		}
	}
	return false;
}


bool CAffixFilterDlg::dlgDelete(POINT pt)
{
	LVHITTESTINFO ht;
	ht.flags = LVHT_ONITEM;
	ht.pt = pt;
	return dlgDelete(CFltList.HitTest(&ht));
}


bool CAffixFilterDlg::dlgDelete(int item)
{
	if ( item >= 0 ) {
		AffixFilter* flt = (AffixFilter*)CFltList.GetItemData(item);
		if ( flt ) {
			if ( flt->formFlags & TESForm::kFormFlags_Deleted ) {
				std::string str = std::string("Do you wish to undelete the AffixFilter'") + flt->GetEditorID() + "'?";
				if ( MessageBox((LPCSTR)str.c_str(), (LPCSTR)"Undelete AffixFilter?", MB_OKCANCEL|MB_ICONQUESTION) == IDOK ) {
					flt->SetDeleted(false);
					flt->SetFromActiveFile(true);
					std::string edid = flt->GetEditorID();
					edid += " *";
					CFltList.SetItemText(item, 0, edid.c_str());
					return true;
				}
				else
					return false;
			}
			else {
				std::string str = std::string("Are you sure you want to delete the AffixFilter '") + flt->GetEditorID() + "'?";
				if ( MessageBox((LPCSTR)str.c_str(), (LPCSTR)"Delete AffixFilter?", MB_OKCANCEL|MB_ICONQUESTION) == IDOK ) {
					flt->SetDeleted(true);
					flt->SetFromActiveFile(true);
					std::string edid = flt->GetEditorID();
					edid += " * D";
					CFltList.SetItemText(item, 0, edid.c_str());
					return true;
				}
				else
					return false;
			}
		}
		else {
			_ERROR("Nulled FltList lParam on row %i.", item);
		}
	}
	return false;
}


void CAffixFilterDlg::OnNMGridModifiedMig(NMHDR *pNMHDR, LRESULT *pResult)
{
	dlgMod(true);
	*pResult = 0;
}


void CAffixFilterDlg::OnNMRClickFltlist(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	CPoint pt = pNMItemActivate->ptAction;
	::ClientToScreen(pNMItemActivate->hdr.hwndFrom, &pt);

	CMenu menu;
	menu.LoadMenu(IDR_NED_MENU);
	CMenu* menuPtr = menu.GetSubMenu(0);
	
	switch (menuPtr->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RETURNCMD, pt.x, pt.y, this))
	{
		case ID_NEW:
			dlgNew();
			break;

		case ID_EDIT:
			dlgEdit(pNMItemActivate->ptAction);
			break;

		case ID_DELETE:
			dlgDelete(pNMItemActivate->ptAction);
			break;

		default:
			break;
	}

	*pResult = 0;
}


void CAffixFilterDlg::OnLvnKeydownFltlist(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLVKEYDOWN pLVKeyDow = reinterpret_cast<LPNMLVKEYDOWN>(pNMHDR);

	switch(pLVKeyDow->wVKey)
	{
		case VK_RETURN:
			dlgEdit(CFltList.GetNextItem(-1, LVNI_FOCUSED|LVNI_SELECTED));
			break;

		case VK_DELETE:
			dlgDelete(CFltList.GetNextItem(-1, LVNI_FOCUSED|LVNI_SELECTED));
			break;
	}

	*pResult = 0;
}


void CAffixFilterDlg::OnBnClickedImport()
{
	char filename[0x400] = {{0}};
	std::string filepath = "Data\\OBSE\\plugins\\ARES";
	if ( TESDialog::ShowFileSelect(	GetSafeHwnd(),
									filepath.c_str(),
									"Tab-delimited file (*.csv; *.dsv; *.txt; *.tsv)\0" "*.csv;*.dsv;*.txt;*.tsv\0" \
										"All files (*.*)\0" "*.*\0",
									"Select file to Import",
									"*.csv",
									0, true, false,
									filename, sizeof(filename))
	) {
		_VMESSAGE("Importing AffixFilters from '%s'.", filename);
		std::ifstream file = std::ifstream(filepath+filename);
		char buf[0x100] = {{0}};
		bool NoToAll = false;
		bool YesToAll = false;
		for ( CSVIterator row(file); row != CSVIterator(); ++row ) {
			if ( row->size() > 47 && (*row)[0].size() > 0 ) {
				strcpy_s(buf, (*row)[0].c_str());
				TESForm::SanitizeEditorIDString(buf);	
				TESForm* tes = TESForm::LookupByEditorID(buf);
				if ( tes ) {
					_WARNING("New AffixFilter '%s' is not unique!\nPrevious form is type %s.", buf, TESForm::GetFormTypeName(tes->GetFormType()));
					continue;
				}

				AffixFilter* new_flt = new AffixFilter;
				new_flt->SetEditorID(buf);
				

				typedef bool (AffixFilter::*textSetFn)(const BSStringT& new_text);
				textSetFn text_fields[] = {
					&AffixFilter::SetNameFilter,
					&AffixFilter::SetModelFilterBipedMale, &AffixFilter::SetModelFilterBipedFemale,
					&AffixFilter::SetModelFilterGroundMale, &AffixFilter::SetModelFilterGroundFemale,
					&AffixFilter::SetIconFilterMale, &AffixFilter::SetIconFilterFemale,
					&AffixFilter::SetModelFilterWeapon, &AffixFilter::SetIconFilterWeapon
				};
				size_t text_cols[] = {
					1,
					29,							30,
					31,							32,
					33,							34,
					43,							44
				};
				BOOST_STATIC_ASSERT_MSG(lengthof(text_fields) == lengthof(text_cols), "text_fields and text_cols do not have the same size.");
				for ( UInt8 i = 0; i < lengthof(text_cols); ++i )
					if ( (*row)[text_cols[i]].size() > 0 )
						(new_flt->*text_fields[i])( (*row)[text_cols[i]].c_str() );
				

				typedef bool (AffixFilter::*numSetFn)(double val);
				numSetFn num_fields[] = {
					&AffixFilter::SetMinValue,			&AffixFilter::SetMaxValue,
					&AffixFilter::SetMinWeight,			&AffixFilter::SetMaxWeight,
					&AffixFilter::SetMinDurability,		&AffixFilter::SetMaxDurability,
					&AffixFilter::SetMinArmorRating,	&AffixFilter::SetMaxArmorRating,
					&AffixFilter::SetMinDamage,			&AffixFilter::SetMaxDamage,
					&AffixFilter::SetMinReach,			&AffixFilter::SetMaxReach,
					&AffixFilter::SetMinSpeed,			&AffixFilter::SetMaxSpeed
				};
				size_t num_cols[] = {
					2,									3,
					4,									5,
					6,									7,
					9,									10,
					36,									37,
					38,									39,
					40,									41
				};
				BOOST_STATIC_ASSERT_MSG(lengthof(num_fields) == lengthof(num_cols), "num_fields and num_cols do not have the same size.");
				for ( UInt8 i = 0; i < lengthof(num_cols); ++i )
					if ( (*row)[num_cols[i]].size() > 0 )
						(new_flt->*num_fields[i])( strToInt((*row)[num_cols[i]]) );


				typedef bool (AffixFilter::*maskSetFn)(UInt8 new_mask);
				maskSetFn mask_fields[] = {
					&AffixFilter::SetApparelMask, &AffixFilter::SetWeaponMask
				};
				size_t mask_cols[] = {
					8, 35
				};
				BOOST_STATIC_ASSERT_MSG(lengthof(mask_fields) == lengthof(mask_cols), "mask_fields and mask_cols do not have the same size.");
				std::string str;
				for ( UInt8 i = 0; i < lengthof(mask_cols); ++i ) {
					str = (*row)[mask_cols[i]];
					if ( str.empty() )
						(new_flt->*mask_fields[i])(0);
					else
						(new_flt->*mask_fields[i])(strToInt(str));
				}
				

				typedef bool (AffixFilter::*triSetFn)(tri check);
				triSetFn tri_fields[] = {
					&AffixFilter::SetHidesRings,	&AffixFilter::SetHidesAmulet,
					&AffixFilter::SetBipedHead,		&AffixFilter::SetBipedHair,			&AffixFilter::SetBipedBodyUpper,	&AffixFilter::SetBipedBodyLower,	&AffixFilter::SetBipedHand,	&AffixFilter::SetBipedFoot,
					&AffixFilter::SetBipedRingRight,&AffixFilter::SetBipedRingLeft,		&AffixFilter::SetBipedAmulet,
					&AffixFilter::SetBipedWeapon,	&AffixFilter::SetBipedWeaponBack,	&AffixFilter::SetBipedWeaponSide,	&AffixFilter::SetBipedQuiver,
					&AffixFilter::SetBipedShield,	&AffixFilter::SetBipedTorch,		&AffixFilter::SetBipedTail,
					&AffixFilter::SetIgnoresNormalWeaponResistance
				};
				size_t tri_cols[] = {
					11,								12,
					13,								14,									15,									16,									17,							18,
					19,								20,									21,
					22,								23,									24,									25,
					26,								27,									28,
					42
				};
				BOOST_STATIC_ASSERT_MSG(lengthof(tri_fields) == lengthof(tri_cols), "tri_fields and tri_cols do not have the same size.");
				for ( UInt8 i = 0; i < lengthof(tri_cols); ++i )
					if ( (*row)[tri_cols[i]].size() > 0 )
						(new_flt->*tri_fields[i])( AffixFilter::intToCheck(strToInt((*row)[tri_cols[i]])) );


				for ( size_t i = 45; i+1 < row->size(); i += 2 ) {
					MagicItemGenerator* mig = dynamic_cast<MagicItemGenerator*>(TESForm::LookupByEditorID((*row)[i].c_str()));
					if ( mig ) {
						if ( new_flt->app && !mig->HasApparelEffects() ) {
							_DMESSAGE("MagicEffectGenerator &lt;%p&gt; has no apparel effects; skipping.", mig->formID);
							continue;
						}
						else if ( new_flt->app ) {
							_DMESSAGE("AffixFilter &lt;%p&gt; uses apparel effects.", new_flt->formID);
						}
						else if ( mig->HasApparelEffects() ) {
							_DMESSAGE("MagicEffectGenerator &lt;%p&gt; has apparel effects; skipping.", mig->formID);
						}
						if ( new_flt->wep && !mig->HasWeaponEffects() ) {
							_DMESSAGE("MagicEffectGenerator &lt;%p&gt; has no weapon effects; skipping.", mig->formID);
							continue;
						}
						else if ( new_flt->wep ) {
							_DMESSAGE("AffixFilter &lt;%p&gt; uses weapon effects.", new_flt->formID);
						}
						else if ( mig->HasWeaponEffects() ) {
							_DMESSAGE("MagicEffectGenerator &lt;%p&gt; has weapon effects; skipping.", mig->formID);
						}
						UInt8 soul = strToInt(std::stringstream((*row)[i+1]));
						if ( soul &= Souls::kSoulMask_All ) {
							NiTListIterator k = new_flt->migs.GetTailPos();
							AffixFilter::Affixes* aff = NULL;
							while ( k ) {
								aff = new_flt->migs.GetNext(k);
								if ( aff && aff->first == mig )
									break;
								aff = NULL;
							}
							if ( aff )
								aff->second |= soul;
							else
								new_flt->migs.AddTail(new AffixFilter::Affixes(mig, soul));
						}
						else {
							_ERROR("Soul value %i for MIG '%s' invalid.", strToInt(std::stringstream((*row)[i+1])), mig->GetEditorID());
						}
					}
					else {
						_ERROR("MIG '%s' not found.", (*row)[i].c_str());
					}
				}
				

				new_flt->SetFromActiveFile(true);
				new_flt->SetFormID(TESDataHandler::dataHandler->ReserveNextUnusedFormID());
				TESDataHandler::dataHandler->AddFormToHandler(static_cast<TESForm*>(new_flt));
				
				LVITEM lv;
				lv.mask = LVIF_TEXT|LVIF_PARAM;
				lv.iItem = -1;
				lv.iSubItem = 0;
				lv.lParam = (LPARAM)new_flt;
				std::string edid = new_flt->GetEditorID();
				edid += " *";
				lv.pszText = (LPSTR)edid.c_str();
				lv.cchTextMax = edid.size();
				CFltList.InsertItem(&lv);
			}
			else {
				_ERROR("Row does not define a valid AffixFilter.");
			}
		}
		file.close();
	}
	else {
		_ERROR("No file imported.");
	}
}


void CAffixFilterDlg::OnTcnSelchangeTabs(NMHDR *pNMHDR, LRESULT *pResult)
{
	// TODO: Add your control notification handler code here
	ShowTab(CFilterTabs.GetCurSel());
	*pResult = 0;
}


void CAffixFilterDlg::ShowTab(int i)
{
	if ( i == currTab )
		return;

	if ( i < 0 || pageCt < i )
		return;

	if ( currTab < 0 || pageCt < currTab ) {
		CRect cltRc;
		CFilterTabs.GetClientRect(&cltRc);
		CFilterTabs.AdjustRect(FALSE, &cltRc);

		CRect wndRc;
		CFilterTabs.GetWindowRect(&wndRc);
		ScreenToClient(&wndRc);
	
		cltRc.OffsetRect(wndRc.left, wndRc.top);

		for ( int j = 0; j < pageCt; ++j )
			tabs[j]->SetWindowPos(&wndTop, cltRc.left-2, cltRc.top, cltRc.Width()+2, cltRc.Height(), (i==j?SWP_SHOWWINDOW:SWP_HIDEWINDOW));
	}
	else
		tabs[currTab]->ShowWindow(SW_HIDE);

	currTab = i;
	tabs[currTab]->ShowWindow(SW_SHOW);
}

#endif
